package com.etonenet.etpe.service;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import com.etonenet.etpe.entity.PhoneApiLog;
import com.etonenet.etpe.jdbc.PhoneMapDao;
import com.etonenet.etpe.util.ApiStatus;

@Service
public class PhoneApiLogService {
	private static Logger logger = LoggerFactory.getLogger(PhoneApiLogService.class);

	private static final int SEQ_LIMIT = 10000;
	private BlockingQueue<PhoneApiLog> phoneApiLogQueue;
	@Autowired
	private SpService spService;
	@Autowired
	private PhoneMapDao phoneMapDao;
	@Autowired
	private ThreadPoolTaskExecutor poolTaskExecutor;

	/**
	 * @param spId
	 * @param phone
	 * @param apiStatus
	 * @param batchNo
	 * @param seq
	 *            [0,10000)
	 * 
	 */
	public void log(long spId, String phone, ApiStatus apiStatus, long batchId, int seq) {
		PhoneApiLog log = new PhoneApiLog();
		long currentTimeMillis = System.currentTimeMillis();
		log.setLogId(getLogId(batchId, seq));
		log.setSpId(spId);
		log.setBatchId(batchId);
		log.setPhone(phone);
		log.setApiTm(new Timestamp(currentTimeMillis));
		log.setApiStatus(apiStatus.getCode());

		// 使用异步日志
		phoneApiLogQueue.add(log);
	}

	/**
	 * 获取LOG ID
	 * 
	 * @param batchId
	 * @param seq
	 * @return
	 */
	private long getLogId(long batchId, int seq) {
		return batchId * SEQ_LIMIT + seq % SEQ_LIMIT;
	}

	public void batchUpdate(List<PhoneApiLog> phoneApiLogs) {
		phoneMapDao.batchUpdate(phoneApiLogs);
	}

	/**
	 * 初始化启动处理线程
	 */
	@PostConstruct
	public void init() {
		phoneApiLogQueue = new LinkedBlockingQueue<>();
		for (int i = 0; i < poolTaskExecutor.getCorePoolSize(); i++) {
			startThread();
		}
	}

	/**
	 * 队列长度超过 1000，或者存活的线程数量小于最少线程数量，则启动一个处理线程
	 */
	@Scheduled(fixedRate = 5000)
	public void activate() {
		logger.debug("poolTaskExecutor active count:{}", poolTaskExecutor.getActiveCount());
		if (logger.isDebugEnabled()) {
			logger.debug("poolTaskExecutor active count:{}", poolTaskExecutor.getActiveCount());
		}

		if (phoneApiLogQueue.size() > 1000 || poolTaskExecutor.getActiveCount() < poolTaskExecutor.getCorePoolSize()) {
			startThread();
		}
	}

	Map<Long, AtomicLong> counterCache = new ConcurrentHashMap<>();

	@Scheduled(fixedRate = 30000)
	private void resetCounterCache() {
		// 累加到数据库
		for (Map.Entry<Long, AtomicLong> item : counterCache.entrySet()) {
			spService.addRationNum(item.getKey(), item.getValue().getAndSet(0));
		}
	}

	/**
	 * 启动一个异步日志处理线程
	 */
	private void startThread() {
		poolTaskExecutor.execute(task);
	}

	private Runnable task = new Runnable() {
		private void save(List<PhoneApiLog> phoneApiLogs) {
			// 计数
			for (PhoneApiLog phoneApiLog : phoneApiLogs) {
				long spId = phoneApiLog.getSpId();
				AtomicLong old = counterCache.get(spId);
				if (old == null) {
					old = new AtomicLong(0);
					counterCache.put(spId, old);
				}
				old.addAndGet(1);
			}

			// 新增日志
			batchUpdate(phoneApiLogs);
			phoneApiLogs.clear();
		}

		public void run() {
			List<PhoneApiLog> phoneApiLogs = Collections.synchronizedList(new ArrayList<PhoneApiLog>());
			while (true) {
				PhoneApiLog log = null;
				try {
					log = phoneApiLogQueue.poll(100, TimeUnit.MILLISECONDS);
				} catch (InterruptedException e) {
					logger.error("{}", ExceptionUtils.getStackTrace(e));
				}
				if (log != null) {
					phoneApiLogs.add(log);
					// 批量保存
					if (phoneApiLogs.size() >= 100) {
						save(phoneApiLogs);
					}
				} else if (phoneApiLogs.size() > 0) {
					save(phoneApiLogs);
					logger.debug("idle...");
					// 当空闲时退出
					break;
				}
			}
		}
	};

	// TODO 数据库定时任务

}
